package pers.qianyu.month_202102.date_20210202;

import org.junit.*;

import java.util.*;
import java.util.stream.*;

/**
 * 399. 除法求值
 * https://leetcode-cn.com/problems/evaluate-division/
 *
 * @author mizzle rain
 * @date 2021-02-02 14:19
 */
public class CalcEquation {
    public double[] calcEquation(List<List<String>> equations,
                                 double[] values,
                                 List<List<String>> queries) {
        HashMap<String, Integer> map = new HashMap<>();
        UnionFind uf = new UnionFind(2 * equations.size());
        for (int i = 0, id = 0, equationsSize = equations.size(); i < equationsSize; i++) {
            List<String> equation = equations.get(i);
            String v1 = equation.get(0);
            String v2 = equation.get(1);
            if (!map.containsKey(v1)) map.put(v1, id++);
            if (!map.containsKey(v2)) map.put(v2, id++);
            uf.union(map.get(v1), map.get(v2), values[i]);
        }
        double[] res = new double[queries.size()];
        int i = 0;
        for (List<String> query : queries) {
            Integer id1 = map.get(query.get(0));
            Integer id2 = map.get(query.get(1));
            if (id1 == null || id2 == null) res[i++] = -1.0;
            else res[i++] = uf.isConnected(id1, id2);
            //System.out.println(uf);
        }
        return res;
    }

    private static class UnionFind {
        private int[] p;
        private double[] w;

        public UnionFind(int n) {
            p = new int[n];
            w = new double[n];
            for (int i = 0; i < n; i++) {
                p[i] = i;
                w[i] = 1.0;
            }
        }

        private int find(int x) {
            if (p[x] != x) {
                int temp = p[x];
                p[x] = find(p[x]);
                w[x] *= w[temp];
            }
            return p[x];
        }

        public double isConnected(int x, int y) {
            int rootX = find(x), rootY = find(y);
            return rootX == rootY ? w[x] / w[y] : -1.0;
        }

        public void union(int x, int y, double value) {
            int rootX = find(x), rootY = find(y);
            if (rootX == rootY) return;
            p[rootX] = rootY;
            w[rootX] = w[y] * value / w[x];
        }

        @Override
        public String toString() {
            return "UnionFind{" +
                    "p=" + Arrays.toString(p) +
                    ", w=" + Arrays.toString(w) +
                    '}';
        }
    }

    @Test
    public void test1() {
        List<List<String>> param1 = Stream.of(
                Stream.of("x1", "x2").collect(Collectors.toList()),
                Stream.of("x2", "x3").collect(Collectors.toList()),
                Stream.of("x3", "x4").collect(Collectors.toList()),
                Stream.of("x4", "x5").collect(Collectors.toList()))
                .collect(Collectors.toList());
        double[] param2 = {3.0, 4.0, 5.0, 6.0};
        List<List<String>> param3 = Stream.of(
                Stream.of("x1", "x5").collect(Collectors.toList()),
                Stream.of("x5", "x2").collect(Collectors.toList()),
                Stream.of("x2", "x4").collect(Collectors.toList()),
                Stream.of("x2", "x2").collect(Collectors.toList()),
                Stream.of("x2", "x9").collect(Collectors.toList()),
                Stream.of("x9", "x9").collect(Collectors.toList()))
                .collect(Collectors.toList());
        double[] res = new CalcEquation().calcEquation(param1, param2, param3);
        System.out.println(Arrays.toString(res));
    }
}
